#include "stdafx.h"
#include "COMPONENTREGISTER.h"
#include <sstream>

#include "..\Infobus\ConnectionString.h"

static CRITICAL_SECTION cs;

ComponentRegister* ComponentRegister::_singleton = NULL;

ComponentRegister::ComponentRegister()
{
  InitializeCriticalSection(&cs);
  initConnection();
}

ComponentRegister::~ComponentRegister()
{
  try { cn->Close(); } catch (...) {}
}

ComponentRegister* ComponentRegister::getInstance()
{
  if (NULL == ComponentRegister::_singleton)
    ComponentRegister::_singleton = new ComponentRegister;
  return ComponentRegister::_singleton;
}

void ComponentRegister::registerComponent(const char* pzsGuid, const char* pszType, const char* pszListeningSubj)
{
  try {
    _bstr_t sql = " INSERT INTO COMPONENT_REGISTER (COMPONENT_GUID, COMPONENT_TYPE, SUBJECT, MASTER) "
                  " SELECT '"+_bstr_t(pzsGuid)+"', '"+_bstr_t(pszType)+"', '"+_bstr_t(pszListeningSubj)+"', 0 ";

    cn->Execute(sql, NULL, ADODB::adExecuteNoRecords);
    if (isRedundant(pzsGuid))
      if (!existsMaster(_bstr_t(pszListeningSubj))) 
        setMaster(_bstr_t(pzsGuid), true);
  } catch (_com_error & e) {
    TCHAR buffer[0x1000] = { 0 };        
    sprintf(buffer, "%s [%ld]\n", e.ErrorMessage(), e.Error());  
    OutputDebugString(buffer);
  } catch (...) { 
    
  }
}

void ComponentRegister::unregisterComponent(const char* pszGuid)
{
  try {
    _bstr_t sql;
    sql = " DELETE FROM COMPONENT_REGISTER "
          " WHERE COMPONENT_GUID = '"+_bstr_t(pszGuid)+"' ";
    cn->Execute(sql, NULL, ADODB::adExecuteNoRecords);

    sql = " DELETE FROM COMPONENT_ACTIVITY "
          " WHERE COMPONENT_GUID = '"+_bstr_t(pszGuid)+"' ";
    cn->Execute(sql, NULL, ADODB::adExecuteNoRecords);
  } catch (_com_error & e) {
    TCHAR buffer[0x1000] = { 0 };        
    sprintf(buffer, "%s [%ld]\n", e.ErrorMessage(), e.Error());  
    OutputDebugString(buffer);
  } catch (...) { 
    
  }
}

void ComponentRegister::activity(const char* pszGuid)
{
  EnterCriticalSection(&cs);
  try {
    _bstr_t sql = " UPDATE COMPONENT_ACTIVITY "
                  " SET LAST_ACTIVITY = getdate() "
                  " WHERE COMPONENT_GUID = '"+_bstr_t(pszGuid)+"' ";

    cn->Execute(sql, NULL, ADODB::adExecuteNoRecords);
  } catch (_com_error & e) {
    TCHAR buffer[0x1000] = { 0 };        
    sprintf(buffer, "%s [%ld]\n", e.ErrorMessage(), e.Error());  
    OutputDebugString(buffer);
  } catch (...) { 
    
  }
  LeaveCriticalSection(&cs);
}

bool ComponentRegister::isMaster(const char* pszGuid)
{
  long lCount;
  HRESULT hr;
  bool bIsMaster = false;

  try {
    _bstr_t sql = " SELECT MASTER FROM COMPONENT_REGISTER "
                  " WHERE COMPONENT_GUID = '"+_bstr_t(pszGuid)+"' ";

    ADODB::_RecordsetPtr rs; 
    hr = rs.CreateInstance (__uuidof(ADODB::Recordset));
    if (S_OK == hr) hr = rs->Open(sql, cn.GetInterfacePtr(), ADODB::adOpenDynamic, ADODB::adLockOptimistic, ADODB::adCmdText);  
    if (S_OK == hr) hr = rs->MoveFirst();
    if (S_OK == hr && !rs->ADOEOF) {
      lCount = (long)rs->Fields->GetItem(L"MASTER")->GetValue();
    }
    rs->Close(); 
  } catch (_com_error & e) {
    TCHAR buffer[0x1000] = { 0 };        
    sprintf(buffer, "%s [%ld]\n", e.ErrorMessage(), e.Error());  
    OutputDebugString(buffer);
  } catch (...) { 
    
  }

  if (0 < lCount) bIsMaster = true;
  else bIsMaster = false;

  return bIsMaster;
}

bool ComponentRegister::isMasterActive(const char* pzsOwnGuid, const char* pszListeningSubj)
{
  long lCount;
  HRESULT hr;
  bool bHasMaster = false;

  DWORD dwSize = 256;
  char szDelay[256];
  ::GetPrivateProfileString("ComponentRegister", "MaxIdleTimeSec", "150" /*2.5 min*/,szDelay,dwSize, "InfobusPrototype.ini");
  ::WritePrivateProfileString("ComponentRegister", "MaxIdleTimeSec", szDelay, "InfobusPrototype.ini");

   try {
    std::stringstream ss;
    ss << " SELECT COUNT(*) AS ANTAL FROM COMPONENT_REGISTER "
       << " WHERE SUBJECT = '" << _bstr_t(pszListeningSubj) << "' "       
       << " AND ( "
       << " 	component_guid IN ( "
       << " 	SELECT component_guid "
       << " 	FROM component_activity "
       << " 	WHERE DATEDIFF(second, LAST_ACTIVITY, getdate()) > " << (atol(szDelay) * 1000) << " "
       << " 	AND MASTER = 1 " 
       << " 	AND SUBJECT = '" << _bstr_t(pszListeningSubj) << "' "
       << "   AND COMPONENT_GUID <> '"<< _bstr_t(pzsOwnGuid) <<"' "
       << " 	) OR component_guid NOT IN ( "
       << " 	SELECT component_guid "
       << " 	FROM component_register "
       << " 	WHERE MASTER = 1 " 
       << " 	AND SUBJECT = '" << _bstr_t(pszListeningSubj) << "' "
       << "   AND COMPONENT_GUID <> '"<< _bstr_t(pzsOwnGuid) <<"' "
       << " 	) "
       << " ) "
       << std::endl;
    
    OutputDebugString(ss.str().c_str());

    ADODB::_RecordsetPtr rs; 
    hr = rs.CreateInstance (__uuidof(ADODB::Recordset));
    if (S_OK == hr) hr = rs->Open(ss.str().c_str(), cn.GetInterfacePtr(), ADODB::adOpenDynamic, ADODB::adLockOptimistic, ADODB::adCmdText);  
    if (S_OK == hr) hr = rs->MoveFirst();
    if (S_OK == hr && !rs->ADOEOF) {
      lCount = (long)rs->Fields->GetItem(L"ANTAL")->GetValue();
    }
    rs->Close(); 

    if (0 < lCount) {
      EnterCriticalSection(&cs);
      setMaster(pzsOwnGuid, true);
      LeaveCriticalSection(&cs);
    }  
  } catch (_com_error & e) {
    TCHAR buffer[0x1000] = { 0 };        
    sprintf(buffer, "%s [%ld]\n", e.ErrorMessage(), e.Error());  
    OutputDebugString(buffer);
  } catch (...) { 
    
  }

  if (0 < lCount) {
    bHasMaster = false;
    if (1 < lCount) {
      // ::MessageBox(NULL, "More than one master exists", "Warning", MB_ICONWARNING | MB_OK);
      OutputDebugString("* More than one master exists\n");
    }
  } else {
    bHasMaster = true;
  }

  return bHasMaster;
}

void ComponentRegister::setMaster(const char* pzsGuid, bool bMaster)
{
  try {
    const char* sql = "UPDATE COMPONENT_REGISTER SET MASTER = 0";

    std::stringstream ss;
    ss << " UPDATE COMPONENT_REGISTER "
       << " SET MASTER = " << (int)bMaster
       << " WHERE COMPONENT_GUID = '"+_bstr_t(pzsGuid)+"' "
       << std::endl;

    OutputDebugString(ss.str().c_str());

    cn->BeginTrans();
    if (bMaster)
      cn->Execute(sql, NULL, ADODB::adExecuteNoRecords);

    cn->Execute(ss.str().c_str(), NULL, ADODB::adExecuteNoRecords);
    cn->CommitTrans();
  } catch (_com_error & e) {
    TCHAR buffer[0x1000] = { 0 };        
    sprintf(buffer, "%s [%ld]\n", e.ErrorMessage(), e.Error());  
    OutputDebugString(buffer);
  } catch (...) { 
    
  }
}

void ComponentRegister::initConnection()
{
  HRESULT hr;
  _bstr_t cns;
  try {
    ::CoInitialize(NULL);
    hr = cn.CreateInstance(__uuidof(ADODB::Connection), NULL, CLSCTX_INPROC_SERVER);  
    if (S_OK != hr) throw("CreateInstance failed");
    cns = bstr_t(CONNECTSTRING);
    hr = S_FALSE;

    while (S_OK != hr) {
      try {
        hr = cn->Open(cns, L"", L"", -1);  
        if (S_OK == hr) cn->AddRef();
        if (S_OK != hr) throw("Database not opened properly");
      } catch (_com_error& e) {
        TCHAR buffer[0x1000] = { 0 };        
        sprintf(buffer, "[%ld] %s\n", e.Error(), e.ErrorMessage());  
        OutputDebugString(buffer);
        try { cn->Close(); } catch (...) { }
        ::Sleep(1000);
      }
    }
  } catch (_com_error & e) {
    TCHAR buffer[0x1000] = { 0 };        
    sprintf(buffer, "%s [%ld]\n", e.ErrorMessage(), e.Error());  
    OutputDebugString(buffer);
    try { cn->Close(); } catch (...) { }
  } catch (...) { 
    
  }
}

bool ComponentRegister::existsMaster(const char* pszListeningSubj)
{
  long lCount;
  HRESULT hr;
  bool bHasMaster = false;

  try {
    _bstr_t sql = " SELECT COUNT(*) AS ANTAL FROM COMPONENT_REGISTER "
                  " WHERE SUBJECT = '"+_bstr_t(pszListeningSubj)+"' "
                  " AND MASTER = 1 ";

    ADODB::_RecordsetPtr rs; 
    hr = rs.CreateInstance (__uuidof(ADODB::Recordset));
    if (S_OK == hr) hr = rs->Open(sql, cn.GetInterfacePtr(), ADODB::adOpenDynamic, ADODB::adLockOptimistic, ADODB::adCmdText);  
    if (S_OK == hr) hr = rs->MoveFirst();
    if (S_OK == hr && !rs->ADOEOF) {
      lCount = (long)rs->Fields->GetItem(L"ANTAL")->GetValue();
    }
    rs->Close(); 
  } catch (_com_error & e) {
    TCHAR buffer[0x1000] = { 0 };        
    sprintf(buffer, "%s [%ld]\n", e.ErrorMessage(), e.Error());  
    OutputDebugString(buffer);
  } catch (...) { 
    
  }

  if (0 < lCount) bHasMaster = true;
  else bHasMaster = false;

  return bHasMaster;
}

void ComponentRegister::enableRedundancy(const char* pzsGuid, const char* pszListeningSubj, bool bEnable)
{

  try {
    std::stringstream ss;
    ss << " UPDATE COMPONENT_REGISTER "
       << " SET ENABLE_REDUNDANCY = " << bEnable << " "
       << " WHERE COMPONENT_GUID = '"+_bstr_t(pzsGuid)+"' ";

    cn->BeginTrans();
    cn->Execute(_bstr_t(ss.str().c_str()), NULL, ADODB::adExecuteNoRecords);
    cn->CommitTrans();
  } catch (_com_error & e) {
    TCHAR buffer[0x1000] = { 0 };        
    sprintf(buffer, "%s [%ld]\n", e.ErrorMessage(), e.Error());  
    OutputDebugString(buffer);
  } catch (...) { 
    
  }

  if (bEnable) {
    if (!existsMaster(_bstr_t(pszListeningSubj))) {
      EnterCriticalSection(&cs);
      setMaster(_bstr_t(pzsGuid), true);
      LeaveCriticalSection(&cs);
    } 
  } else {  
    EnterCriticalSection(&cs);  
    setMaster(_bstr_t(pzsGuid), false);
    LeaveCriticalSection(&cs);
  }  
}

bool ComponentRegister::isRedundant(const char* pzsOwnGuid)
{
  long lCount;
  HRESULT hr;
  bool bIsRedundant = false;

  try {
    _bstr_t sql = " SELECT COUNT(*) AS ANTAL FROM COMPONENT_REGISTER "
                  " WHERE COMPONENT_GUID = '"+_bstr_t(pzsOwnGuid)+"' "
                  " AND ENABLE_REDUNDANCY = 1 ";

    ADODB::_RecordsetPtr rs; 
    hr = rs.CreateInstance (__uuidof(ADODB::Recordset));
    if (S_OK == hr) hr = rs->Open(sql, cn.GetInterfacePtr(), ADODB::adOpenDynamic, ADODB::adLockOptimistic, ADODB::adCmdText);  
    if (S_OK == hr) hr = rs->MoveFirst();
    if (S_OK == hr && !rs->ADOEOF) {
      lCount = (long)rs->Fields->GetItem(L"ANTAL")->GetValue();
    }
    rs->Close(); 
  } catch (_com_error & e) {
    TCHAR buffer[0x1000] = { 0 };        
    sprintf(buffer, "%s [%ld]\n", e.ErrorMessage(), e.Error());  
    OutputDebugString(buffer);
  } catch (...) { 
    
  }

  if (0 < lCount) bIsRedundant = true;
  else bIsRedundant = false;

  return bIsRedundant;
}